package com.shop.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.stream.StreamUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.shop.dao.OrderItemDao;
import com.shop.dao.OrderMasterDao;
import com.shop.dao.OrderNoteDao;
import com.shop.dao.OrderPayDao;
import com.shop.dto.*;
import com.shop.dto.enums.OrderMasterStatus;
import com.shop.dto.enums.OrderPayStatus;
import com.shop.entity.*;
import com.shop.local.UserInfoLocal;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


/**
 * @author wzby
 * @Date: 2022/10/21 9:58
 */
@Service
public class OrderingService {

    @Resource(name = "redisTemplate")
    private HashOperations<String, String, CartEntity> hashOperations;

    @Resource(name = "redisTemplate")
    private RedisTemplate redisTemplate;

    @Autowired
    private ProductService productService;
    @Autowired
    private CartService cartService;

    @Autowired
    private OrderMasterDao orderMasterDao;
    @Autowired
    private OrderItemDao orderItemDao;
    @Autowired
    private OrderNoteDao orderNoteDao;
    @Autowired
    private OrderPayDao orderPayDao;

    private final static String LOCK_memberId_Order_KEY = "member.order.lock.{}";
    private final static String cartKEY = "cart.{}";

    public OrderingOkDto ordering(OrderingDto orderingDto) throws Exception {
        // 订单防重
        //1. 前段防重, 按钮点击后立即禁用
        //2.用户一般在5秒之内基本上只能提交一个订单
        Integer memberId = UserInfoLocal.get().getMemberId();
        String lockKey = StrUtil.format(LOCK_memberId_Order_KEY, memberId);
        Boolean existLockKey = redisTemplate.hasKey(lockKey);
        // 判断existLockKey是否存在
        if (existLockKey) {
            throw new Exception("提交订单过于频繁");
        }
        //到这个位置,说明用户还没有提交过订单
        //加一个5秒的锁，锁的单位是用户
        redisTemplate.opsForValue().set(lockKey, DateUtil.now(), 5, TimeUnit.SECONDS);

        // 防重购物车
        String myCartKey = StrUtil.format(cartKEY, memberId);
        //将orderingDto.getProductIds()转化为String
        List<String> products = Arrays.stream(orderingDto.getProductIds()).map(item -> item.toString()).collect(Collectors.toList());

        List<CartEntity> cartEntities = new ArrayList<>();
        List<CartEntity> cartEntities2 = hashOperations.multiGet(myCartKey, products);
        cartEntities2.stream().forEach(item -> {
            if (item != null) {
                cartEntities.add(item);
            }
        });

        if (ObjectUtil.isEmpty(cartEntities)) {
            throw new Exception("购物车里没有商品");
        }
        //将cartEntities中的标识id循环出来
        List<Long> ids = CollStreamUtil.toList(cartEntities, CartEntity::getId).stream().sorted().collect(Collectors.toList());
        String cartIdSKey = "cart.lock.ids." + StrUtil.join("-", ids);
        Boolean existCartIdSKey = redisTemplate.hasKey(cartIdSKey);
        // 判断existCartIdSKey是否存在
        if (existCartIdSKey) {
            throw new Exception("购物车订单请勿重复提交");
        }
        redisTemplate.opsForValue().set(lockKey, DateUtil.now(), 30, TimeUnit.SECONDS);

        //正常业务往下走
        //金币验证
        Integer deductionCoin = orderingDto.getDeductionCoin();
        if (deductionCoin > 0) {
            //您的金币余额不足以本次订单金币抵消
            /**
             *  金币余额代码
             */
        }
        Long orderId = getNewOrderId();
        //扣除库存
        /**
         *  扣除库存代码
         */
        //订单明细orderItem
        List<OrderItemEntity> orderItemEntities = new ArrayList<>(cartEntities.size());
        for (CartEntity cartEntity : cartEntities) {
            OrderItemEntity.OrderItemEntityBuilder itemEntityBuilder = OrderItemEntity.builder();
            itemEntityBuilder.orderId(orderId);
            itemEntityBuilder.price(cartEntity.getPrice());
            itemEntityBuilder.productId(cartEntity.getProductId());
            itemEntityBuilder.productImg(cartEntity.getProductImg());
            itemEntityBuilder.productName(cartEntity.getProductName());
            itemEntityBuilder.qty(cartEntity.getQty());
            orderItemEntities.add(itemEntityBuilder.build());
        }

        //购物订单orderMaster
        double totalPrice = cartEntities.stream().mapToDouble(item -> NumberUtil.mul(item.getPrice(), item.getQty()).doubleValue()).sum();
        double needPay = totalPrice;
        //当金币余额正好抵消本次订单的支付消费
        if (orderingDto.getDeductionCoin() > 0) {
            BigDecimal coin = BigDecimal.valueOf(orderingDto.getDeductionCoin());
            BigDecimal coinPrice = NumberUtil.mul(coin, 0.01);
            needPay = NumberUtil.sub(BigDecimal.valueOf(needPay), coinPrice).doubleValue();
        }

        OrderMasterEntity.OrderMasterEntityBuilder masterEntityBuilder = OrderMasterEntity.builder();
        masterEntityBuilder.id(orderId);
        masterEntityBuilder.memberId(memberId);
        masterEntityBuilder.phone(UserInfoLocal.get().getPhone());
        masterEntityBuilder.addressId(orderingDto.getAddressId());
        masterEntityBuilder.createTime(LocalDateTime.now());
        //本次订单支付金额大于0时，订单状态为下单成功; 否则订单状态为支付成功
        if (needPay > 0) {
            masterEntityBuilder.status(OrderMasterStatus.MASTER_STATUS_ORDERINGOK.getCode());
        } else {
            masterEntityBuilder.status(OrderMasterStatus.MASTER_STATUS_PAYOK.getCode());
        }
        masterEntityBuilder.totalPrice(BigDecimal.valueOf(totalPrice));
        masterEntityBuilder.sourceFrom(orderingDto.getSourceFrom());
        masterEntityBuilder.products(orderItemEntities);
        OrderMasterEntity orderMasterEntity = masterEntityBuilder.build();

        //开始组装订单支付
        OrderPayEntity.OrderPayEntityBuilder payEntityBuilder = OrderPayEntity.builder();
        if (orderingDto.getDeductionCoin() > 0) {
            payEntityBuilder.orderId(orderId);
            BigDecimal coin = BigDecimal.valueOf(orderingDto.getDeductionCoin());
            payEntityBuilder.qty(coin);
            payEntityBuilder.amount(NumberUtil.mul(coin, 0.01));
            payEntityBuilder.type(OrderPayStatus.COIN_PAY.getCode());
            payEntityBuilder.brief(OrderPayStatus.COIN_PAY.getName());
            payEntityBuilder.createTime(LocalDateTime.now());
            payEntityBuilder.seq(5);
        }
        OrderPayEntity orderPayEntity = payEntityBuilder.build();

        //组装订单日志
        OrderNoteEntity orderNoteEntity = new OrderNoteEntity();
        orderNoteEntity.setOrderId(orderId);
        //当支付金额为0时,订单日志里的状态和备注为支付成功;否则订单日志里的状态和备注为下单成功
        if (needPay == 0) {
            orderNoteEntity.setStatus(OrderMasterStatus.MASTER_STATUS_PAYOK.getCode());
            orderNoteEntity.setBrief(OrderMasterStatus.MASTER_STATUS_PAYOK.getName());
        } else {
            orderNoteEntity.setStatus(OrderMasterStatus.MASTER_STATUS_ORDERINGOK.getCode());
            orderNoteEntity.setBrief(OrderMasterStatus.MASTER_STATUS_ORDERINGOK.getName());
        }
        orderNoteEntity.setLastUpdateBy(UserInfoLocal.get().getPhone());

        //将订单数据插入数据库表中
        OrderingService orderingService = (OrderingService) AopContext.currentProxy();
        orderingService.insertDB(orderMasterEntity, orderPayEntity, orderNoteEntity);
        OrderingOkDto orderingOkDto = new OrderingOkDto();
        orderingOkDto.setOrderId(orderId);
        orderingOkDto.setMemberId(memberId);
        orderingOkDto.setNeedPay(BigDecimal.valueOf(needPay));
        orderingOkDto.setCoins(orderingDto.getDeductionCoin());

        //发送到队列

        //清空购物车
        //将cartEntities中的商品id循环出来
        List<Integer> productIds = cartEntities.stream().map(item -> item.getProductId()).collect(Collectors.toList());
        cartService.clearCart(memberId, productIds);

        return orderingOkDto;
    }

    @Transactional
    public void insertDB(OrderMasterEntity orderMaster, OrderPayEntity orderPay, OrderNoteEntity orderNote) {
        orderMasterDao.insert(orderMaster);
        orderItemDao.insertAll(orderMaster.getProducts());
        orderPayDao.insert(orderPay);
        orderNoteDao.insert(orderNote);
    }

    private Long getNewOrderId() {
        String yyMMddHHmmss = DateUtil.format(DateUtil.date(), "yyMMddHHmmss");
        String newKey = "order.id." + yyMMddHHmmss;
        Long id = redisTemplate.opsForValue().increment(newKey, 1);
        String orderIdStr = StrUtil.fillBefore(id.toString(), '0', 3);
        String orderId = yyMMddHHmmss + orderIdStr;
        Long newOrderId = Long.valueOf(orderId);
        return newOrderId;
    }

}
